using System;
using System.Collections.Generic;
using System.Text;
using Core.Domain.Bases;
using Core.Domain.Enums;

namespace Core.Domain.Entities
{
  ///<summary>
  ///  Core information about a person
  ///</summary>
  public class Person : EntityBaseWithId
  {
    private IndependentWrapper<bool> _adminRightsRemoved;
    private IndependentWrapper<AgeGroup> _ageGroup;
    private IndependentWrapper<string> _bahaiId;
    private IndependentWrapper<DateTime> _birthDate;
    private IndependentWrapper<Community> _community;
    private IndependentWrapper<ElectionParticipationMethod> _electionParticipationMethod;
    private IndependentWrapper<string> _firstName;
    private IndependentWrapper<string> _lastName;
    private IndependentWrapper<string> _otherFirstNames;
    private IndependentWrapper<string> _otherLastNames;

    ///<summary>
    ///  Information about the core identity of a person
    ///</summary>
    public Person()
    {
      this.CreateIndependentInstances();
      AgeGroup = AgeGroup.Adult;
    }

    ///<summary>
    ///</summary>
    public virtual string BahaiId
    {
      get { return _bahaiId.Value; }
      set { _bahaiId.Value = value; }
    }

    ///<summary>
    ///</summary>
    public virtual string FirstName
    {
      get { return _firstName.Value; }
      set { _firstName.Value = value; }
    }

    ///<summary>
    ///</summary>
    public virtual string LastName
    {
      get { return _lastName.Value; }
      set { _lastName.Value = value; }
    }

    ///<summary>
    ///</summary>
    public virtual string OtherFirstNames
    {
      get { return _otherFirstNames.Value; }
      set { _otherFirstNames.Value = value; }
    }

    ///<summary>
    ///</summary>
    public virtual string OtherLastNames
    {
      get { return _otherLastNames.Value; }
      set { _otherLastNames.Value = value; }
    }

    ///<summary>
    ///</summary>
    public virtual DateTime BirthDate
    {
      get { return _birthDate.Value; }
      set { _birthDate.Value = value; }
    }

    ///<summary>
    ///</summary>
    public virtual AgeGroup AgeGroup
    {
      get { return _ageGroup.Value; }
      set { _ageGroup.Value = value; }
    }

    ///<summary>
    ///  If rights removed, cannot vote or be voted for
    ///</summary>
    public virtual bool AdminRightsRemoved
    {
      get { return _adminRightsRemoved.Value; }
      set { _adminRightsRemoved.Value = value; }
    }

    ///<summary>
    ///</summary>
    public virtual Community Community
    {
      get { return _community.Value; }
      set { _community.Value = value; }
    }

    public ElectionParticipationMethod ElectionParticipationMethod
    {
      get { return _electionParticipationMethod.Value; }
      set { _electionParticipationMethod.Value = value; }
    }

    #region Nested type: CompareByVoteeId

    public class CompareByVoteeId : IEqualityComparer<Person>
    {
      #region IEqualityComparer<Person> Members

      public bool Equals(Person x, Person y)
      {
        if (x == null | y == null)
        {
          return false;
        }

        return x.VoteeId() == y.VoteeId();
      }

      public int GetHashCode(Person obj)
      {
        return obj.VoteeId().GetHashCode();
      }

      #endregion
    }

    #endregion
  }

  ///<summary>
  ///  Extensions for Person
  ///</summary>
  public static partial class Extensions
  {
    ///<summary>
    ///  A unique identifier for this person, used in Ballots when this person is voted for.
    ///</summary>
    ///<param name = "input"></param>
    ///<returns></returns>
    public static string VoteeId(this Person input)
    {
      if (input == null)
      {
        return string.Empty;
      }
      return string.Format("{0}~{1}~{2}~{3}~{4}", input.LastName, input.FirstName, input.BahaiId, input.OtherLastNames,
                           input.OtherFirstNames);
    }

    ///<summary>
    ///  A displayable name for this person
    ///</summary>
    ///<param name = "input"></param>
    ///<returns></returns>
    public static string DisplayName(this Person input)
    {
      return DisplayName(input, false);
    }

    public static string Participation(this Person input)
    {
      if(input==null) return string.Empty;
      return input.ElectionParticipationMethod.ToString();
    }

    ///<summary>
    ///  An adult in good standing
    ///</summary>
    ///<param name = "input"></param>
    ///<returns></returns>
    public static bool IsAdultInGoodStanding(this Person input)
    {
      return !input.AdminRightsRemoved && input.AgeGroup == AgeGroup.Adult;
    }

    ///<summary>
    ///  Change this person's AgeGroup to match their birthdate, as of today
    ///</summary>
    ///<param name = "input"></param>
    public static AgeGroup SetAgeGroup(this Person input)
    {
      SetAgeGroup(input, DateTime.Today);
      return input.AgeGroup;
    }

    ///<summary>
    ///  Change this person's AgeGroup to match their birthdate, as of the given date. This should be run regularly!
    ///</summary>
    ///<param name = "input"></param>
    ///<param name = "asOfDate">Date to determine age at</param>
    public static AgeGroup SetAgeGroup(this Person input, DateTime asOfDate)
    {
      var ageGroup = AgeGroup.DetermineAgeGroup(input, asOfDate);
      input.AgeGroup = ageGroup;
      return ageGroup;
    }

    ///<summary>
    ///  A displayable name for this person
    ///</summary>
    ///<param name = "input"></param>
    ///<param name = "showOtherNames">If true, any other names are included</param>
    ///<returns></returns>
    public static string DisplayName(this Person input, bool showOtherNames)
    {
      if (input == null)
      {
        return string.Empty;
      }

      var sb = new StringBuilder(input.FirstName.DefaultingTo(" ")
                                 + " "
                                 + input.LastName.DefaultingTo("?"));

      if (showOtherNames)
      {
        sb.Append(input.OtherNames());
      }

      return sb.ToString();
    }

    ///<summary>
    ///  When sorting by last name, use this version. Not good for display.
    ///</summary>
    ///<param name = "input"></param>
    ///<returns></returns>
    public static string SortableName(this Person input)
    {
      return string.Format("{0}, {1} [{2}, {3}]", input.LastName, input.FirstName, input.OtherLastNames,
                           input.OtherFirstNames);
    }

    ///<summary>
    ///  Combine the other first and last names for display
    ///</summary>
    ///<param name = "input"></param>
    ///<returns></returns>
    public static string OtherNames(this Person input)
    {
      if (input == null)
      {
        return string.Empty;
      }

      var otherFirst = input.OtherFirstNames.HasContent();
      var otherLast = input.OtherLastNames.HasContent();

      if ((otherFirst || otherLast))
      {
        var sb = new StringBuilder();

        sb.Append(" [");
        sb.Append(input.OtherFirstNames);
        if (otherFirst && otherLast)
        {
          sb.Append(", ");
        }
        sb.Append(input.OtherLastNames);
        sb.Append("]");

        return sb.ToString();
      }

      return string.Empty;
    }
  }
}